package com.sisyphe.android.srouter.compiler

import com.google.auto.service.AutoService
import com.sisyphe.android.srouter.compiler.builder.IProviderBuilder
import com.sisyphe.android.srouter.compiler.builder.IRouteBuilder
import com.sisyphe.android.srouter.compiler.builder.ProviderBuilderImpl
import com.sisyphe.android.srouter.compiler.builder.RouteBuilderImpl
import com.sisyphe.android.srouter.facade.annotation.Provider
import com.sisyphe.android.srouter.facade.annotation.Route
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Filer
import javax.annotation.processing.Messager
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic

@AutoService(Processor::class)
class SRouterAnnotationProcessor : AbstractProcessor() {

    private lateinit var messager: Messager

    private lateinit var filer: Filer

    private lateinit var elementUtils: Elements

    private lateinit var typeUtils: Types

    private lateinit var moduleName: String

    private lateinit var sourceVersion: SourceVersion

    private val providerBuilder: IProviderBuilder by lazy { ProviderBuilderImpl(filer, messager, sourceVersion, moduleName, elementUtils, typeUtils) }

    private val routeBuilder: IRouteBuilder by lazy { RouteBuilderImpl(filer, messager) }

    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)

        if (null != processingEnv) {
            this.messager = processingEnv.messager
            this.filer = processingEnv.filer
            this.elementUtils = processingEnv.elementUtils
            this.typeUtils = processingEnv.typeUtils
            val moduleName = processingEnv.options["kapt.srouter.module.name"]
            if (null == moduleName) {
                messager.printMessage(
                    Diagnostic.Kind.ERROR, """
                        |
                        |please add the following script:
                        |
                        |    kapt {
                        |        arguments {
                        |            arg("kapt.srouter.module.name", project.name)
                        |        }
                        |    }
                        |    
                    """.trimMargin(), null
                )
            } else {
                this.moduleName = moduleName
            }
            this.sourceVersion = processingEnv.sourceVersion
            if (supportedSourceVersion != sourceVersion) {
                messager.printMessage(
                    Diagnostic.Kind.ERROR, """
                        |
                      |please use $supportedSourceVersion to compile the $moduleName module, you can add the following script:
                      |
                      |    android {
                      |        ......
                      |        compileOptions {
                      |            sourceCompatibility = JavaVersion.VERSION_17
                      |            targetCompatibility = JavaVersion.VERSION_17
                      |        }
                      |    }
                      |    
                """.trimMargin(), null
                )
                return
            }
        } else {
            return
        }
    }

    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(
            Route::class.java.canonicalName,
            Provider::class.java.canonicalName
        )
    }

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latest()
    }

    override fun process(annotations: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean {
        if (false == roundEnvironment?.processingOver() && true == annotations?.isNotEmpty()) {
            val providerSet = roundEnvironment.getElementsAnnotatedWith(Provider::class.java)
            if (providerSet.isNotEmpty()) {
                messager.printMessage(Diagnostic.Kind.NOTE, "Provider processor start! \n", null)
                providerSet.forEach {
                    providerBuilder.collect(it)
                }
                providerBuilder.build()
                messager.printMessage(Diagnostic.Kind.NOTE, "Provider processor end! \n", null)
            }

            val routeSet = roundEnvironment.getElementsAnnotatedWith(Route::class.java)
            if (routeSet.isNotEmpty()) {
                messager.printMessage(Diagnostic.Kind.NOTE, "Route processor start! \n", null)
                routeSet.forEach {
                    routeBuilder.collect(it)
                }
                routeBuilder.build()
                messager.printMessage(Diagnostic.Kind.NOTE, "Route processor end! \n", null)
            }
        }

        return false
    }

}